package jude.java.money.net.http;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class HttpDownloader {
  public final static int OK = 0;
  public final static int FAILED = -1;
  public final static int HTTP_EXCEPTION = -2;
  public final static int IO_EXCEPTION = -3;
  
  private final static int HTTP_TIMEOUT = 60000;
  private final static String DEFAULT_ENCODING = "GB2312";
  
  private static final Log LOG = LogFactory.getLog(HttpDownloader.class);
  
  /**
   * 通过HTTP协议下载文件
   * @param url 文件URL地址
   * @param params 参数
   * @param targetFilePath 保存路径
   * @return 编码
   * @throws HttpException
   * @throws IOException
   */
  public static String download(String url, Map<String, String> params, String targetFilePath) throws Exception{
    return download(null, 0, null, null, url, params, targetFilePath, true);
  }
  
  public static String download(String[] proxyConfigs, String url, Map<String, String> params, String targetFilePath) throws Exception{
    if(proxyConfigs!= null && proxyConfigs.length >= 4)
      return download(proxyConfigs[0], Integer.parseInt(proxyConfigs[1]), proxyConfigs[2], proxyConfigs[3], url, params, targetFilePath, true);
    return download(null, 0, null, null, url, params, targetFilePath, true);
  }
  
  private static String download(String proxyHost, int proxyPort, String proxyUsr, String proxyPwd
      , String url, Map<String, String> params, String targetFilePath, boolean redirect) throws Exception{
    LOG.info("start to download: " + url);
    HttpClient httpClient = getProxyHttpClient(proxyHost, proxyPort, proxyUsr, proxyPwd, HTTP_TIMEOUT);//
    GetMethod method = new GetMethod(url);
    
//应用服务器认证
//    httpClient.getState().setCredentials(AuthScope.ANY
//        , new UsernamePasswordCredentials("tomcat", "tomcat"));
//    method.setDoAuthentication(true);

    method.setFollowRedirects(redirect);
    if(params != null){
      for(Map.Entry<String, String> param : params.entrySet()){
        method.getParams().setParameter(param.getKey(), param.getValue());
      }
    }
    
    setRequestHeader(method);
    
    int statusCode = httpClient.executeMethod(method);
	if(redirect && statusCode == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED){//407
		return download(proxyHost, proxyPort, proxyUsr, proxyPwd, url, params, targetFilePath, false);
	}
	if(!redirect && isRedirect(statusCode)){
		Header header = method.getResponseHeader("Location");
		if (header != null && StringUtils.isNotEmpty(header.getValue())) {
			return download(proxyHost, proxyPort, proxyUsr, proxyPwd, header.getValue(), params, targetFilePath, false);
		}
	}

    if(statusCode != HttpStatus.SC_OK){
      LOG.error("Can't download file from " + url);
      LOG.error("http server response code:" + statusCode);
      throw new RuntimeException("Can't download file from " + url);
    }
    
    renderFile(method, targetFilePath);
    LOG.info("download file success. save as " + targetFilePath);
    String charset = method.getResponseCharSet();
    if("ISO-8859-1".equalsIgnoreCase(charset))  //未指定编码时返回ISO-8859-1, 改为默认的GB2312
      charset = DEFAULT_ENCODING;
    return charset;
  }
  
	private static boolean isRedirect(int statusCode) {
		if ((statusCode == HttpStatus.SC_MOVED_TEMPORARILY) ||
            (statusCode == HttpStatus.SC_MOVED_PERMANENTLY) ||
            (statusCode == HttpStatus.SC_SEE_OTHER) ||
            (statusCode == HttpStatus.SC_TEMPORARY_REDIRECT)) {
        	return true;
        } else {
        	return false;
        }
	}
	
  protected static int handleProxyRedirect(HttpClient httpClient, HttpMethodBase method) throws NullPointerException, HttpException, IOException{
	  Header header = method.getResponseHeader("Location");
	  if(header != null && StringUtils.isNotEmpty(header.getValue())){
		  method.setURI(new URI(header.getValue(), true));
		  method.releaseConnection();
		  method.setFollowRedirects(false);
		  return httpClient.executeMethod(method);
	  }
	  return -1; 
  }
  
  protected static void logHttpHeader(HttpMethodBase method){
	  if(method == null)
		  return;
	  Header qhs[] = method.getRequestHeaders();
	  for (Header header : qhs) {
		LOG.info("request header: " + header.getName() + "=" + header.getValue());
	  }
	  Header phs[] = method.getResponseHeaders();
	  for (Header header : phs) {
		LOG.info("response header: " + header.getName() + "=" + header.getValue());
	  }
	  
  }
  
  protected static String getContent(String[] proxyConfigs, String url, String encoding) throws HttpException, IOException{
    HttpClient httpClient = getProxyHttpClient(proxyConfigs[0], Integer.parseInt(proxyConfigs[1]), proxyConfigs[2], proxyConfigs[3], HTTP_TIMEOUT);
    GetMethod method = new GetMethod(url);
    int statusCode = httpClient.executeMethod(method);
    if(statusCode != HttpStatus.SC_OK){
      return null;
    }
    String charset = null;
    if(StringUtils.isEmpty(encoding)){
    	charset = method.getRequestCharSet();
    	if("ISO-8859-1".equalsIgnoreCase(charset))  //未指定编码时返回ISO-8859-1, 改为默认的GB2312
    		charset = DEFAULT_ENCODING;
  }
    return new String(method.getResponseBody(), charset);
  }
  
  private static HttpClient getProxyHttpClient(String host, int port, String usr, String pwd, int timeout) {
    HttpClient httpClient = new HttpClient();
    if(timeout > 0){
      httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(timeout);
      httpClient.getHttpConnectionManager().getParams().setSoTimeout(timeout);
    }
    if (StringUtils.isEmpty(host))
      return httpClient;

    if(StringUtils.isNotEmpty(usr)){
      UsernamePasswordCredentials creds = null;
      if(usr.indexOf('\\') > -1)
        creds = new NTCredentials(StringUtils.substringAfter(usr, "\\"), pwd, host, StringUtils.substringBefore(usr, "\\"));
      else
        creds = new UsernamePasswordCredentials(usr, pwd);
      httpClient.getState().setProxyCredentials(AuthScope.ANY, creds);
    }

    HostConfiguration hcf = new HostConfiguration();
    hcf.setProxy(host, port);
    httpClient.setHostConfiguration(hcf);
    
    return httpClient;
  }
  
  private static void renderFile(HttpMethodBase method, String filePath) throws IOException{
    InputStream input = method.getResponseBodyAsStream();

    byte[] buffer = new byte[8192];
    int length = 0;
    BufferedInputStream in = new BufferedInputStream(input);
    BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(new File(filePath)));
    int total = 0;
    while((length = in.read(buffer)) > 0){
      out.write(buffer, 0, length);
      total += length;
    }
    if(total == 0){
    	LOG.error("can't get remote data(0 byte(s)): " + method.getPath());
    	throw new RuntimeException("can't get remote data: " + method.getPath());
    }
    out.close();
  }
  
  private final static String urlPattern = "^[hH][tT][tT][pP][sS]?\\://.+";
  /**
   * 获取完整的URL路径
   * @param root 根路径
   * @param path 文件路径
   */
  public static String getUrl(String root, String path){
    if(root == null){
      root = "http://";
    }
    if(!path.matches(urlPattern)){
      boolean a = root.endsWith("/");
      boolean b = path.startsWith("/");
      if(a && b)
        return root + path.substring(1);
      if(!a && !b)
        return root + "/" + path;
      return root + path;
    }
    return path;
  }
  
  /**从URL上获取文件名*/
  public static String getFileName(String url){
    int index = url.indexOf('?');
    if(index >= 0)
      url = url.substring(0, index);
    if(url.endsWith("/"))
      url = url.substring(0, url.length()-1);
    if(url.lastIndexOf('/') >= 0)
      url = url.substring(url.lastIndexOf('/')+1);
//    System.out.println(url);
    return url;
  }
  
  public static void setRequestHeader(HttpMethodBase method){
	  method.addRequestHeader("accept", "*/*");
	  method.addRequestHeader("accept-language", "zh-cn");
//	  method.addRequestHeader("ua-cpu", "x86");
//	  method.addRequestHeader("accept-encoding", "gzip, deflate");
	  method.addRequestHeader("user-agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)");
	  //method.addRequestHeader("host", "localhost:8080");
	  method.addRequestHeader("connection", "Keep-Alive");
//	  method.addRequestHeader("cookie", "");
  }
}
